package com.rw.coco.handler.message.reply.handler.command.sender;

import com.rw.coco.api.data.model.CharacterPropertyModel;
import com.rw.coco.api.data.model.GroupModel;
import com.rw.coco.api.data.persistence.GroupMapper;
import com.rw.coco.api.event.GroupMessageEvent;
import com.rw.coco.handler.message.event.CocoMessageEvent;
import com.rw.coco.handler.message.reply.handler.command.assistant.ra.SuccessLevel;
import com.rw.coco.handler.message.reply.handler.command.bean.RAResult;
import com.rw.coco.handler.message.reply.handler.command.bean.RARule;
import com.rw.coco.handler.message.reply.handler.command.exception.global.NumberOverflowException;
import com.rw.coco.handler.message.reply.handler.command.exception.ra.*;
import com.rw.coco.service.CharacterPropertyService;
import com.rw.coco.util.DiceRule;
import com.rw.tool.util.random.RandomUtil;
import com.rw.tool.util.string.StringUtil;
import lombok.Getter;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

@Getter
public class RASender extends BaseSender {
    private RARule parseRARule;

    public RASender(CocoMessageEvent event) {
        super(event);
    }

    public String send(String rawCommand, DiceRule diceRule) {
        // 获取用户的所有属性
        CharacterPropertyService propertyService = new CharacterPropertyService(mapper);
        Set<CharacterPropertyModel> allProp = propertyService.getAllProp(accountModel);

        try {
            // 解析指令，获得检定规则的对象
            RARule raRule = RARule.parse(rawCommand, diceRule, allProp, anotherNameConfig);
            this.parseRARule = raRule;

            // 使用检定规则的对象进行检定
            List<RAResult> raResultList = rollDetermine(raRule);

            // 返回检定结果
            return diceReply.raResult(event, raRule, raResultList);
        } catch (CommandEmptyException exception) {
            // 如果指令为空，则返回空异常
            event.sendBack(diceReply.raEmpty(event));
        } catch (PropNotFoundException exception) {
            // 如果对应属性未找到，则返回未找到异常
            String propName = exception.getPropName();
            event.sendBack(diceReply.raNotFindProp(event, propName));
        } catch (CommandTailException exception) {
            // 如果后续指令异常，则返回解析错误
            String errorCommand = exception.getRawCommand();
            int index = exception.getIndex();
            event.sendBack(diceReply.raCommandError(event, errorCommand, index));
        } catch (TooManyCountException exception) {
            // 如果检定次数过多，则抛出异常
            event.sendBack(diceReply.tooManyCount(event, exception.getCount(), exception.getMaxCount()));
        } catch (TooManyBonusesException exception) {
            // 如果设置的奖励/惩罚骰过多，则抛出异常
            event.sendBack(diceReply.tooManyBonuses(event, exception.getCount(), exception.getMaxCount()));
        } catch (PropNumOverflow exception) {
            // 如果设置的属性值超过限定，则抛出异常
            event.sendBack(diceReply.propNumTooBig(event, exception.getNum(), exception.getMaxNum()));
        } catch (NumberOverflowException exception) {
            // 数字超大
            event.sendBack(diceReply.numberOverflowException(event));
        }

        return StringUtil.EMPTY;
    }

    /**
     * 获取群组的房规
     *
     * @return 返回房规规则
     */
    public DiceRule getDiceRule(boolean isRA) {
        // 如果是群组，则获取房规
        if (event instanceof GroupMessageEvent && isRA) {
            // 获取群组信息
            String groupId = ((GroupMessageEvent) event).getGroup().getId();
            GroupMapper groupMapper = event.getDataApi().getPersistenceMapper().getGroupMapper();
            GroupModel groupModel = groupMapper.getGroupOrPut(groupId);

            // 获取房规
            return DiceRule.of(groupModel.getDiceRule());
        }

        return DiceRule.STANDARD_RULE;
    }

    /**
     * 使用指定的ROLL点规则进行检定
     *
     * @param raRule roll点规则
     */
    private List<RAResult> rollDetermine(RARule raRule) {
        List<RAResult> raResultList = new ArrayList<>(raRule.getCount());

        // 多重检定
        for (int i = 0; i < raRule.getCount(); i++) {
            RAResult raResult = rollOnce(raRule);
            raResultList.add(raResult);
        }

        return raResultList;
    }

    /**
     * 随机一次，结算结果并且返回
     *
     * @param raRule 随机规则
     * @return 随机结果
     */
    private RAResult rollOnce(RARule raRule) {
        int bonusesDiceCount = raRule.getBonusesDice();

        // 随机出结果
        int randomValue = RandomUtil.nextInt(100) + 1;
        // 如果随机的结果以0为结尾，则奖励骰范围从0-9改为1-10
        boolean bonusesPlus = randomValue % 10 == 0;

        // 获得所有的奖励骰子
        int size = Math.abs(bonusesDiceCount);
        List<Integer> bonusesDice = getBonusesDice(size, bonusesPlus);

        // 计算出最终结果
        boolean isBonuses = bonusesDiceCount >= 0;
        int result = calcValue(randomValue, bonusesDice, isBonuses);

        // 计算成功等级
        SuccessLevel successLevel = raRule.getDiceRule().roll(raRule.getValue(), result);

        // 返回结果
        return new RAResult(isBonuses, bonusesDice, randomValue, result, successLevel);
    }

    /**
     * roll出所有的奖励骰子
     *
     * @param size        扔奖励骰子的个数
     * @param bonusesPlus 奖励骰子的范围是否需要+1
     * @return 奖励骰子列表
     */
    private List<Integer> getBonusesDice(int size, boolean bonusesPlus) {
        List<Integer> bonusesDiceList = new ArrayList<>(size);

        // roll出所有奖励/惩罚骰
        for (int i = 0; i < size; i++) {
            // 如果随机的结果以0为结尾，则奖励骰范围从0-9改为1-10
            int bonusesDice = RandomUtil.nextInt(10) + (bonusesPlus ? 1 : 0);
            bonusesDiceList.add(bonusesDice);
        }

        return bonusesDiceList;
    }

    /**
     * 根据奖励骰列表，计算出最终结果
     *
     * @return 最终结果
     */
    private int calcValue(int randomNum, List<Integer> bonusesDice, boolean isBonuses) {
        // 获得随机数的个位和十位
        int tenDigit = randomNum / 10;
        int singleDigit = randomNum % 10;

        // 复制列表，并加入原随机数的十位
        List<Integer> copyBonuses = new ArrayList<>(bonusesDice);
        copyBonuses.add(tenDigit);

        // 最终结果的十位
        int resultTenDigit;
        // 根据isBonuses取出最大或者最小值
        if (isBonuses) {
            resultTenDigit = copyBonuses.stream().min(Integer::compareTo).orElse(tenDigit);
        } else {
            resultTenDigit = copyBonuses.stream().max(Integer::compareTo).orElse(tenDigit);
        }

        // 返回最终结果
        return resultTenDigit * 10 + singleDigit;
    }
}
